home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / lib / hplip / base / exif.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2007-04-29  |  26.5 KB  |  929 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.5)
  3.  
  4. FIELD_TYPES = ((0, 'X', 'Proprietary'), (1, 'B', 'Byte'), (1, 'A', 'ASCII'), (2, 'S', 'Short'), (4, 'L', 'Long'), (8, 'R', 'Ratio'), (1, 'SB', 'Signed Byte'), (1, 'U', 'Undefined'), (2, 'SS', 'Signed Short'), (4, 'SL', 'Signed Long'), (8, 'SR', 'Signed Ratio'))
  5. EXIF_TAGS = {
  6.     256: ('ImageWidth',),
  7.     257: ('ImageLength',),
  8.     258: ('BitsPerSample',),
  9.     259: ('Compression', {
  10.         1: 'Uncompressed TIFF',
  11.         6: 'JPEG Compressed' }),
  12.     262: ('PhotometricInterpretation',),
  13.     266: ('FillOrder',),
  14.     269: ('DocumentName',),
  15.     270: ('ImageDescription',),
  16.     271: ('Make',),
  17.     272: ('Model',),
  18.     273: ('StripOffsets',),
  19.     274: ('Orientation',),
  20.     277: ('SamplesPerPixel',),
  21.     278: ('RowsPerStrip',),
  22.     279: ('StripByteCounts',),
  23.     282: ('XResolution',),
  24.     283: ('YResolution',),
  25.     284: ('PlanarConfiguration',),
  26.     296: ('ResolutionUnit', {
  27.         1: 'Not Absolute',
  28.         2: 'Pixels/Inch',
  29.         3: 'Pixels/Centimeter' }),
  30.     301: ('TransferFunction',),
  31.     305: ('Software',),
  32.     306: ('DateTime',),
  33.     315: ('Artist',),
  34.     318: ('WhitePoint',),
  35.     319: ('PrimaryChromaticities',),
  36.     342: ('TransferRange',),
  37.     512: ('JPEGProc',),
  38.     513: ('JPEGInterchangeFormat',),
  39.     514: ('JPEGInterchangeFormatLength',),
  40.     529: ('YCbCrCoefficients',),
  41.     530: ('YCbCrSubSampling',),
  42.     531: ('YCbCrPositioning',),
  43.     532: ('ReferenceBlackWhite',),
  44.     33421: ('CFARepeatPatternDim',),
  45.     33422: ('CFAPattern',),
  46.     33423: ('BatteryLevel',),
  47.     33432: ('Copyright',),
  48.     33434: ('ExposureTime',),
  49.     33437: ('FNumber',),
  50.     33723: ('IPTC/NAA',),
  51.     34665: ('ExifOffset',),
  52.     34675: ('InterColorProfile',),
  53.     34850: ('ExposureProgram', {
  54.         0: 'Unidentified',
  55.         1: 'Manual',
  56.         2: 'Program Normal',
  57.         3: 'Aperture Priority',
  58.         4: 'Shutter Priority',
  59.         5: 'Program Creative',
  60.         6: 'Program Action',
  61.         7: 'Portrait Mode',
  62.         8: 'Landscape Mode' }),
  63.     34852: ('SpectralSensitivity',),
  64.     34853: ('GPSInfo',),
  65.     34855: ('ISOSpeedRatings',),
  66.     34856: ('OECF',),
  67.     36864: ('ExifVersion', (lambda x: ''.join(map(chr, x)))),
  68.     36867: ('DateTimeOriginal',),
  69.     36868: ('DateTimeDigitized',),
  70.     37121: ('ComponentsConfiguration', {
  71.         0: '',
  72.         1: 'Y',
  73.         2: 'Cb',
  74.         3: 'Cr',
  75.         4: 'Red',
  76.         5: 'Green',
  77.         6: 'Blue' }),
  78.     37122: ('CompressedBitsPerPixel',),
  79.     37377: ('ShutterSpeedValue',),
  80.     37378: ('ApertureValue',),
  81.     37379: ('BrightnessValue',),
  82.     37380: ('ExposureBiasValue',),
  83.     37381: ('MaxApertureValue',),
  84.     37382: ('SubjectDistance',),
  85.     37383: ('MeteringMode', {
  86.         0: 'Unidentified',
  87.         1: 'Average',
  88.         2: 'CenterWeightedAverage',
  89.         3: 'Spot',
  90.         4: 'MultiSpot' }),
  91.     37384: ('LightSource', {
  92.         0: 'Unknown',
  93.         1: 'Daylight',
  94.         2: 'Fluorescent',
  95.         3: 'Tungsten',
  96.         10: 'Flash',
  97.         17: 'Standard Light A',
  98.         18: 'Standard Light B',
  99.         19: 'Standard Light C',
  100.         20: 'D55',
  101.         21: 'D65',
  102.         22: 'D75',
  103.         255: 'Other' }),
  104.     37385: ('Flash', {
  105.         0: 'No',
  106.         1: 'Fired',
  107.         5: 'Fired (?)',
  108.         7: 'Fired (!)',
  109.         9: 'Fill Fired',
  110.         13: 'Fill Fired (?)',
  111.         15: 'Fill Fired (!)',
  112.         16: 'Off',
  113.         24: 'Auto Off',
  114.         25: 'Auto Fired',
  115.         29: 'Auto Fired (?)',
  116.         31: 'Auto Fired (!)',
  117.         32: 'Not Available' }),
  118.     37386: ('FocalLength',),
  119.     37500: ('MakerNote',),
  120.     37510: ('UserComment', (lambda x: ''.join(map(chr, x)))),
  121.     37520: ('SubSecTime',),
  122.     37521: ('SubSecTimeOriginal',),
  123.     37522: ('SubSecTimeDigitized',),
  124.     40960: ('FlashPixVersion', (lambda x: ''.join(map(chr, x)))),
  125.     40961: ('ColorSpace',),
  126.     40962: ('ExifImageWidth',),
  127.     40963: ('ExifImageLength',),
  128.     40965: ('InteroperabilityOffset',),
  129.     41483: ('FlashEnergy',),
  130.     41484: ('SpatialFrequencyResponse',),
  131.     41486: ('FocalPlaneXResolution',),
  132.     41487: ('FocalPlaneYResolution',),
  133.     41488: ('FocalPlaneResolutionUnit',),
  134.     41492: ('SubjectLocation',),
  135.     41493: ('ExposureIndex',),
  136.     41495: ('SensingMethod',),
  137.     41728: ('FileSource', {
  138.         3: 'Digital Camera' }),
  139.     41729: ('SceneType', {
  140.         1: 'Directly Photographed' }) }
  141. INTR_TAGS = {
  142.     1: ('InteroperabilityIndex',),
  143.     2: ('InteroperabilityVersion',),
  144.     4096: ('RelatedImageFileFormat',),
  145.     4097: ('RelatedImageWidth',),
  146.     4098: ('RelatedImageLength',) }
  147. GPS_TAGS = {
  148.     0: ('GPSVersionID',),
  149.     1: ('GPSLatitudeRef',),
  150.     2: ('GPSLatitude',),
  151.     3: ('GPSLongitudeRef',),
  152.     4: ('GPSLongitude',),
  153.     5: ('GPSAltitudeRef',),
  154.     6: ('GPSAltitude',),
  155.     7: ('GPSTimeStamp',),
  156.     8: ('GPSSatellites',),
  157.     9: ('GPSStatus',),
  158.     10: ('GPSMeasureMode',),
  159.     11: ('GPSDOP',),
  160.     12: ('GPSSpeedRef',),
  161.     13: ('GPSSpeed',),
  162.     14: ('GPSTrackRef',),
  163.     15: ('GPSTrack',),
  164.     16: ('GPSImgDirectionRef',),
  165.     17: ('GPSImgDirection',),
  166.     18: ('GPSMapDatum',),
  167.     19: ('GPSDestLatitudeRef',),
  168.     20: ('GPSDestLatitude',),
  169.     21: ('GPSDestLongitudeRef',),
  170.     22: ('GPSDestLongitude',),
  171.     23: ('GPSDestBearingRef',),
  172.     24: ('GPSDestBearing',),
  173.     25: ('GPSDestDistanceRef',),
  174.     26: ('GPSDestDistance',) }
  175. MAKERNOTE_NIKON_NEWER_TAGS = {
  176.     2: ('ISOSetting',),
  177.     3: ('ColorMode',),
  178.     4: ('Quality',),
  179.     5: ('Whitebalance',),
  180.     6: ('ImageSharpening',),
  181.     7: ('FocusMode',),
  182.     8: ('FlashSetting',),
  183.     15: ('ISOSelection',),
  184.     128: ('ImageAdjustment',),
  185.     130: ('AuxiliaryLens',),
  186.     133: ('ManualFocusDistance',),
  187.     134: ('DigitalZoomFactor',),
  188.     136: ('AFFocusPosition', {
  189.         0: 'Center',
  190.         256: 'Top',
  191.         512: 'Bottom',
  192.         768: 'Left',
  193.         1024: 'Right' }),
  194.     148: ('Saturation', {
  195.         -3: 'B&W',
  196.         -2: '-2',
  197.         -1: '-1',
  198.         0: '0',
  199.         1: '1',
  200.         2: '2' }),
  201.     149: ('NoiseReduction',),
  202.     16: ('DataDump',) }
  203. MAKERNOTE_NIKON_OLDER_TAGS = {
  204.     3: ('Quality', {
  205.         1: 'VGA Basic',
  206.         2: 'VGA Normal',
  207.         3: 'VGA Fine',
  208.         4: 'SXGA Basic',
  209.         5: 'SXGA Normal',
  210.         6: 'SXGA Fine' }),
  211.     4: ('ColorMode', {
  212.         1: 'Color',
  213.         2: 'Monochrome' }),
  214.     5: ('ImageAdjustment', {
  215.         0: 'Normal',
  216.         1: 'Bright+',
  217.         2: 'Bright-',
  218.         3: 'Contrast+',
  219.         4: 'Contrast-' }),
  220.     6: ('CCDSpeed', {
  221.         0: 'ISO 80',
  222.         2: 'ISO 160',
  223.         4: 'ISO 320',
  224.         5: 'ISO 100' }),
  225.     7: ('WhiteBalance', {
  226.         0: 'Auto',
  227.         1: 'Preset',
  228.         2: 'Daylight',
  229.         3: 'Incandescent',
  230.         4: 'Fluorescent',
  231.         5: 'Cloudy',
  232.         6: 'Speed Light' }) }
  233.  
  234. def olympus_special_mode(v):
  235.     a = {
  236.         0: 'Normal',
  237.         1: 'Unknown',
  238.         2: 'Fast',
  239.         3: 'Panorama' }
  240.     b = {
  241.         0: 'Non-panoramic',
  242.         1: 'Left to right',
  243.         2: 'Right to left',
  244.         3: 'Bottom to top',
  245.         4: 'Top to bottom' }
  246.     return '%s - sequence %d - %s' % (a[v[0]], v[1], b[v[2]])
  247.  
  248. MAKERNOTE_OLYMPUS_TAGS = {
  249.     256: ('JPEGThumbnail',),
  250.     512: ('SpecialMode', olympus_special_mode),
  251.     513: ('JPEGQual', {
  252.         1: 'SQ',
  253.         2: 'HQ',
  254.         3: 'SHQ' }),
  255.     514: ('Macro', {
  256.         0: 'Normal',
  257.         1: 'Macro' }),
  258.     516: ('DigitalZoom',),
  259.     519: ('SoftwareRelease',),
  260.     520: ('PictureInfo',),
  261.     521: ('CameraID', (lambda x: ''.join(map(chr, x)))),
  262.     3840: ('DataDump',) }
  263. MAKERNOTE_CASIO_TAGS = {
  264.     1: ('RecordingMode', {
  265.         1: 'Single Shutter',
  266.         2: 'Panorama',
  267.         3: 'Night Scene',
  268.         4: 'Portrait',
  269.         5: 'Landscape' }),
  270.     2: ('Quality', {
  271.         1: 'Economy',
  272.         2: 'Normal',
  273.         3: 'Fine' }),
  274.     3: ('FocusingMode', {
  275.         2: 'Macro',
  276.         3: 'Auto Focus',
  277.         4: 'Manual Focus',
  278.         5: 'Infinity' }),
  279.     4: ('FlashMode', {
  280.         1: 'Auto',
  281.         2: 'On',
  282.         3: 'Off',
  283.         4: 'Red Eye Reduction' }),
  284.     5: ('FlashIntensity', {
  285.         11: 'Weak',
  286.         13: 'Normal',
  287.         15: 'Strong' }),
  288.     6: ('Object Distance',),
  289.     7: ('WhiteBalance', {
  290.         1: 'Auto',
  291.         2: 'Tungsten',
  292.         3: 'Daylight',
  293.         4: 'Fluorescent',
  294.         5: 'Shade',
  295.         129: 'Manual' }),
  296.     11: ('Sharpness', {
  297.         0: 'Normal',
  298.         1: 'Soft',
  299.         2: 'Hard' }),
  300.     12: ('Contrast', {
  301.         0: 'Normal',
  302.         1: 'Low',
  303.         2: 'High' }),
  304.     13: ('Saturation', {
  305.         0: 'Normal',
  306.         1: 'Low',
  307.         2: 'High' }),
  308.     20: ('CCDSpeed', {
  309.         64: 'Normal',
  310.         80: 'Normal',
  311.         100: 'High',
  312.         125: '+1.0',
  313.         244: '+3.0',
  314.         250: '+2.0' }) }
  315. MAKERNOTE_FUJIFILM_TAGS = {
  316.     0: ('NoteVersion', (lambda x: ''.join(map(chr, x)))),
  317.     4096: ('Quality',),
  318.     4097: ('Sharpness', {
  319.         1: 'Soft',
  320.         2: 'Soft',
  321.         3: 'Normal',
  322.         4: 'Hard',
  323.         5: 'Hard' }),
  324.     4098: ('WhiteBalance', {
  325.         0: 'Auto',
  326.         256: 'Daylight',
  327.         512: 'Cloudy',
  328.         768: 'DaylightColor-Fluorescent',
  329.         769: 'DaywhiteColor-Fluorescent',
  330.         770: 'White-Fluorescent',
  331.         1024: 'Incandescent',
  332.         3840: 'Custom' }),
  333.     4099: ('Color', {
  334.         0: 'Normal',
  335.         256: 'High',
  336.         512: 'Low' }),
  337.     4100: ('Tone', {
  338.         0: 'Normal',
  339.         256: 'High',
  340.         512: 'Low' }),
  341.     4112: ('FlashMode', {
  342.         0: 'Auto',
  343.         1: 'On',
  344.         2: 'Off',
  345.         3: 'Red Eye Reduction' }),
  346.     4113: ('FlashStrength',),
  347.     4128: ('Macro', {
  348.         0: 'Off',
  349.         1: 'On' }),
  350.     4129: ('FocusMode', {
  351.         0: 'Auto',
  352.         1: 'Manual' }),
  353.     4144: ('SlowSync', {
  354.         0: 'Off',
  355.         1: 'On' }),
  356.     4145: ('PictureMode', {
  357.         0: 'Auto',
  358.         1: 'Portrait',
  359.         2: 'Landscape',
  360.         4: 'Sports',
  361.         5: 'Night',
  362.         6: 'Program AE',
  363.         256: 'Aperture Priority AE',
  364.         512: 'Shutter Priority AE',
  365.         768: 'Manual Exposure' }),
  366.     4352: ('MotorOrBracket', {
  367.         0: 'Off',
  368.         1: 'On' }),
  369.     4864: ('BlurWarning', {
  370.         0: 'Off',
  371.         1: 'On' }),
  372.     4865: ('FocusWarning', {
  373.         0: 'Off',
  374.         1: 'On' }),
  375.     4866: ('AEWarning', {
  376.         0: 'Off',
  377.         1: 'On' }) }
  378. MAKERNOTE_CANON_TAGS = {
  379.     6: ('ImageType',),
  380.     7: ('FirmwareVersion',),
  381.     8: ('ImageNumber',),
  382.     9: ('OwnerName',) }
  383. MAKERNOTE_CANON_TAG_0x001 = {
  384.     1: ('Macromode', {
  385.         1: 'Macro',
  386.         2: 'Normal' }),
  387.     2: ('SelfTimer',),
  388.     3: ('Quality', {
  389.         2: 'Normal',
  390.         3: 'Fine',
  391.         5: 'Superfine' }),
  392.     4: ('FlashMode', {
  393.         0: 'Flash Not Fired',
  394.         1: 'Auto',
  395.         2: 'On',
  396.         3: 'Red-Eye Reduction',
  397.         4: 'Slow Synchro',
  398.         5: 'Auto + Red-Eye Reduction',
  399.         6: 'On + Red-Eye Reduction',
  400.         16: 'external flash' }),
  401.     5: ('ContinuousDriveMode', {
  402.         0: 'Single Or Timer',
  403.         1: 'Continuous' }),
  404.     7: ('FocusMode', {
  405.         0: 'One-Shot',
  406.         1: 'AI Servo',
  407.         2: 'AI Focus',
  408.         3: 'MF',
  409.         4: 'Single',
  410.         5: 'Continuous',
  411.         6: 'MF' }),
  412.     10: ('ImageSize', {
  413.         0: 'Large',
  414.         1: 'Medium',
  415.         2: 'Small' }),
  416.     11: ('EasyShootingMode', {
  417.         0: 'Full Auto',
  418.         1: 'Manual',
  419.         2: 'Landscape',
  420.         3: 'Fast Shutter',
  421.         4: 'Slow Shutter',
  422.         5: 'Night',
  423.         6: 'B&W',
  424.         7: 'Sepia',
  425.         8: 'Portrait',
  426.         9: 'Sports',
  427.         10: 'Macro/Close-Up',
  428.         11: 'Pan Focus' }),
  429.     12: ('DigitalZoom', {
  430.         0: 'None',
  431.         1: '2x',
  432.         2: '4x' }),
  433.     13: ('Contrast', {
  434.         65535: 'Low',
  435.         0: 'Normal',
  436.         1: 'High' }),
  437.     14: ('Saturation', {
  438.         65535: 'Low',
  439.         0: 'Normal',
  440.         1: 'High' }),
  441.     15: ('Sharpness', {
  442.         65535: 'Low',
  443.         0: 'Normal',
  444.         1: 'High' }),
  445.     16: ('ISO', {
  446.         0: 'See ISOSpeedRatings Tag',
  447.         15: 'Auto',
  448.         16: '50',
  449.         17: '100',
  450.         18: '200',
  451.         19: '400' }),
  452.     17: ('MeteringMode', {
  453.         3: 'Evaluative',
  454.         4: 'Partial',
  455.         5: 'Center-weighted' }),
  456.     18: ('FocusType', {
  457.         0: 'Manual',
  458.         1: 'Auto',
  459.         3: 'Close-Up (Macro)',
  460.         8: 'Locked (Pan Mode)' }),
  461.     19: ('AFPointSelected', {
  462.         12288: 'None (MF)',
  463.         12289: 'Auto-Selected',
  464.         12290: 'Right',
  465.         12291: 'Center',
  466.         12292: 'Left' }),
  467.     20: ('ExposureMode', {
  468.         0: 'Easy Shooting',
  469.         1: 'Program',
  470.         2: 'Tv-priority',
  471.         3: 'Av-priority',
  472.         4: 'Manual',
  473.         5: 'A-DEP' }),
  474.     23: ('LongFocalLengthOfLensInFocalUnits',),
  475.     24: ('ShortFocalLengthOfLensInFocalUnits',),
  476.     25: ('FocalUnitsPerMM',),
  477.     28: ('FlashActivity', {
  478.         0: 'Did Not Fire',
  479.         1: 'Fired' }),
  480.     29: ('FlashDetails', {
  481.         14: 'External E-TTL',
  482.         13: 'Internal Flash',
  483.         11: 'FP Sync Used',
  484.         7: '2nd("Rear")-Curtain Sync Used',
  485.         4: 'FP Sync Enabled' }),
  486.     32: ('FocusMode', {
  487.         0: 'Single',
  488.         1: 'Continuous' }) }
  489. MAKERNOTE_CANON_TAG_0x004 = {
  490.     7: ('WhiteBalance', {
  491.         0: 'Auto',
  492.         1: 'Sunny',
  493.         2: 'Cloudy',
  494.         3: 'Tungsten',
  495.         4: 'Fluorescent',
  496.         5: 'Flash',
  497.         6: 'Custom' }),
  498.     9: ('SequenceNumber',),
  499.     14: ('AFPointUsed',),
  500.     15: ('FlashBias', {
  501.         65472: '-2 EV',
  502.         65484: '-1.67 EV',
  503.         65488: '-1.50 EV',
  504.         65492: '-1.33 EV',
  505.         65504: '-1 EV',
  506.         65516: '-0.67 EV',
  507.         65520: '-0.50 EV',
  508.         65524: '-0.33 EV',
  509.         0: '0 EV',
  510.         12: '0.33 EV',
  511.         16: '0.50 EV',
  512.         20: '0.67 EV',
  513.         32: '1 EV',
  514.         44: '1.33 EV',
  515.         48: '1.50 EV',
  516.         52: '1.67 EV',
  517.         64: '2 EV' }),
  518.     19: ('SubjectDistance',) }
  519.  
  520. def s2n_motorola(str):
  521.     x = 0
  522.     for c in str:
  523.         x = x << 8 | ord(c)
  524.     
  525.     return x
  526.  
  527.  
  528. def s2n_intel(str):
  529.     x = 0
  530.     y = 0x0L
  531.     for c in str:
  532.         x = x | ord(c) << y
  533.         y = y + 8
  534.     
  535.     return x
  536.  
  537.  
  538. def gcd(a, b):
  539.     if b == 0:
  540.         return a
  541.     else:
  542.         return gcd(b, a % b)
  543.  
  544.  
  545. class Ratio:
  546.     
  547.     def __init__(self, num, den):
  548.         self.num = num
  549.         self.den = den
  550.  
  551.     
  552.     def __repr__(self):
  553.         self.reduce()
  554.         if self.den == 1:
  555.             return str(self.num)
  556.         
  557.         return '%d/%d' % (self.num, self.den)
  558.  
  559.     
  560.     def reduce(self):
  561.         div = gcd(self.num, self.den)
  562.         if div > 1:
  563.             self.num = self.num / div
  564.             self.den = self.den / div
  565.         
  566.  
  567.  
  568.  
  569. class IFD_Tag:
  570.     
  571.     def __init__(self, printable, tag, field_type, values, field_offset, field_length):
  572.         self.printable = printable
  573.         self.tag = tag
  574.         self.field_type = field_type
  575.         self.field_offset = field_offset
  576.         self.field_length = field_length
  577.         self.values = values
  578.  
  579.     
  580.     def __str__(self):
  581.         return self.printable
  582.  
  583.     
  584.     def __repr__(self):
  585.         return '(0x%04X) %s=%s @ %d' % (self.tag, FIELD_TYPES[self.field_type][2], self.printable, self.field_offset)
  586.  
  587.  
  588.  
  589. class EXIF_header:
  590.     
  591.     def __init__(self, file, endian, offset, debug = 0):
  592.         self.file = file
  593.         self.endian = endian
  594.         self.offset = offset
  595.         self.debug = debug
  596.         self.tags = { }
  597.  
  598.     
  599.     def s2n(self, offset, length, signed = 0):
  600.         self.file.seek(self.offset + offset)
  601.         slice = self.file.read(length)
  602.         if self.endian == 'I':
  603.             val = s2n_intel(slice)
  604.         else:
  605.             val = s2n_motorola(slice)
  606.         if signed:
  607.             pass
  608.         
  609.         return val
  610.  
  611.     
  612.     def n2s(self, offset, length):
  613.         s = ''
  614.         for i in range(length):
  615.             if self.endian == 'I':
  616.                 s = s + chr(offset & 255)
  617.             else:
  618.                 s = chr(offset & 255) + s
  619.             offset = offset >> 8
  620.         
  621.         return s
  622.  
  623.     
  624.     def first_IFD(self):
  625.         return self.s2n(4, 4)
  626.  
  627.     
  628.     def next_IFD(self, ifd):
  629.         entries = self.s2n(ifd, 2)
  630.         return self.s2n(ifd + 2 + 12 * entries, 4)
  631.  
  632.     
  633.     def list_IFDs(self):
  634.         i = self.first_IFD()
  635.         a = []
  636.         while i:
  637.             a.append(i)
  638.             i = self.next_IFD(i)
  639.         return a
  640.  
  641.     
  642.     def dump_IFD(self, ifd, ifd_name, dict = EXIF_TAGS):
  643.         entries = self.s2n(ifd, 2)
  644.         for i in range(entries):
  645.             entry = ifd + 2 + 12 * i
  646.             tag = self.s2n(entry, 2)
  647.             field_type = self.s2n(entry + 2, 2)
  648.             if field_type < field_type:
  649.                 pass
  650.             elif not field_type < len(FIELD_TYPES):
  651.                 raise ValueError, 'unknown type %d in tag 0x%04X' % (field_type, tag)
  652.             
  653.             typelen = FIELD_TYPES[field_type][0]
  654.             count = self.s2n(entry + 4, 4)
  655.             offset = entry + 8
  656.             if count * typelen > 4:
  657.                 offset = self.s2n(offset, 4)
  658.             
  659.             field_offset = offset
  660.             if field_type == 2:
  661.                 if count != 0:
  662.                     self.file.seek(self.offset + offset)
  663.                     values = self.file.read(count).strip().replace('\x00', '')
  664.                 else:
  665.                     values = ''
  666.             else:
  667.                 values = []
  668.                 signed = field_type in (6, 8, 9, 10)
  669.                 for j in range(count):
  670.                     if field_type in (5, 10):
  671.                         value_j = Ratio(self.s2n(offset, 4, signed), self.s2n(offset + 4, 4, signed))
  672.                     else:
  673.                         value_j = self.s2n(offset, typelen, signed)
  674.                     values.append(value_j)
  675.                     offset = offset + typelen
  676.                 
  677.             if count == 1 and field_type != 2:
  678.                 printable = str(values[0])
  679.             else:
  680.                 printable = str(values)
  681.             tag_entry = dict.get(tag)
  682.             if tag_entry:
  683.                 tag_name = tag_entry[0]
  684.                 if len(tag_entry) != 1:
  685.                     if callable(tag_entry[1]):
  686.                         printable = tag_entry[1](values)
  687.                     else:
  688.                         printable = ''
  689.                         for i in values:
  690.                             printable += tag_entry[1].get(i, repr(i))
  691.                         
  692.                 
  693.             else:
  694.                 tag_name = 'Tag 0x%04X' % tag
  695.             self.tags[ifd_name + ' ' + tag_name] = IFD_Tag(printable, tag, field_type, values, field_offset, count * typelen)
  696.             if self.debug:
  697.                 print '    %s: %s' % (tag_name, repr(self.tags[ifd_name + ' ' + tag_name]))
  698.                 continue
  699.         
  700.  
  701.     
  702.     def extract_TIFF_thumbnail(self, thumb_ifd):
  703.         entries = self.s2n(thumb_ifd, 2)
  704.         if self.endian == 'M':
  705.             tiff = 'MM\x00*\x00\x00\x00\x08'
  706.         else:
  707.             tiff = 'II*\x00\x08\x00\x00\x00'
  708.         self.file.seek(self.offset + thumb_ifd)
  709.         tiff += self.file.read(entries * 12 + 2) + '\x00\x00\x00\x00'
  710.         for i in range(entries):
  711.             entry = thumb_ifd + 2 + 12 * i
  712.             tag = self.s2n(entry, 2)
  713.             field_type = self.s2n(entry + 2, 2)
  714.             typelen = FIELD_TYPES[field_type][0]
  715.             count = self.s2n(entry + 4, 4)
  716.             oldoff = self.s2n(entry + 8, 4)
  717.             ptr = i * 12 + 18
  718.             if tag == 273:
  719.                 strip_off = ptr
  720.                 strip_len = count * typelen
  721.             
  722.             if count * typelen > 4:
  723.                 newoff = len(tiff)
  724.                 tiff = tiff[:ptr] + self.n2s(newoff, 4) + tiff[ptr + 4:]
  725.                 if tag == 273:
  726.                     strip_off = newoff
  727.                     strip_len = 4
  728.                 
  729.                 self.file.seek(self.offset + oldoff)
  730.                 tiff += self.file.read(count * typelen)
  731.                 continue
  732.         
  733.         old_offsets = self.tags['Thumbnail StripOffsets'].values
  734.         old_counts = self.tags['Thumbnail StripByteCounts'].values
  735.         for i in range(len(old_offsets)):
  736.             offset = self.n2s(len(tiff), strip_len)
  737.             tiff = tiff[:strip_off] + offset + tiff[strip_off + strip_len:]
  738.             strip_off += strip_len
  739.             self.file.seek(self.offset + old_offsets[i])
  740.             tiff += self.file.read(old_counts[i])
  741.         
  742.         self.tags['TIFFThumbnail'] = tiff
  743.  
  744.     
  745.     def decode_maker_note(self):
  746.         note = self.tags['EXIF MakerNote']
  747.         make = self.tags['Image Make'].printable
  748.         model = self.tags['Image Model'].printable
  749.         if make == 'NIKON':
  750.             if note.values[0:5] == [
  751.                 78,
  752.                 105,
  753.                 107,
  754.                 111,
  755.                 110]:
  756.                 self.dump_IFD(note.field_offset + 8, 'MakerNote', dict = MAKERNOTE_NIKON_OLDER_TAGS)
  757.             else:
  758.                 self.dump_IFD(note.field_offset, 'MakerNote', dict = MAKERNOTE_NIKON_NEWER_TAGS)
  759.             return None
  760.         
  761.         if make[:7] == 'OLYMPUS':
  762.             self.dump_IFD(note.field_offset + 8, 'MakerNote', dict = MAKERNOTE_OLYMPUS_TAGS)
  763.             return None
  764.         
  765.         if make == 'Casio':
  766.             self.dump_IFD(note.field_offset, 'MakerNote', dict = MAKERNOTE_CASIO_TAGS)
  767.             return None
  768.         
  769.         if make == 'FUJIFILM':
  770.             endian = self.endian
  771.             self.endian = 'I'
  772.             offset = self.offset
  773.             self.offset += note.field_offset
  774.             self.dump_IFD(12, 'MakerNote', dict = MAKERNOTE_FUJIFILM_TAGS)
  775.             self.endian = endian
  776.             self.offset = offset
  777.             return None
  778.         
  779.         if make == 'Canon':
  780.             self.dump_IFD(note.field_offset, 'MakerNote', dict = MAKERNOTE_CANON_TAGS)
  781.             for i in (('MakerNote Tag 0x0001', MAKERNOTE_CANON_TAG_0x001), ('MakerNote Tag 0x0004', MAKERNOTE_CANON_TAG_0x004)):
  782.                 self.canon_decode_tag(self.tags[i[0]].values, i[1])
  783.             
  784.             return None
  785.         
  786.  
  787.     
  788.     def canon_decode_tag(self, value, dict):
  789.         for i in range(1, len(value)):
  790.             x = dict.get(i, ('Unknown',))
  791.             if self.debug:
  792.                 print i, x
  793.             
  794.             name = x[0]
  795.             if len(x) > 1:
  796.                 val = x[1].get(value[i], 'Unknown')
  797.             else:
  798.                 val = value[i]
  799.             self.tags['MakerNote ' + name] = IFD_Tag(str(val), None, 0, None, None, None)
  800.         
  801.  
  802.  
  803.  
  804. def process_file(file, debug = 0):
  805.     data = file.read(12)
  806.     if data[0:4] in ('II*\x00', 'MM\x00*'):
  807.         file.seek(0)
  808.         endian = file.read(1)
  809.         file.read(1)
  810.         offset = 0
  811.     elif data[0:2] == '\xff\xd8':
  812.         while data[2] == '\xff' and data[6:10] in ('JFIF', 'JFXX', 'OLYM'):
  813.             length = ord(data[4]) * 256 + ord(data[5])
  814.             file.read(length - 8)
  815.             data = '\xff\x00' + file.read(10)
  816.         if data[2] == '\xff' and data[6:10] == 'Exif':
  817.             offset = file.tell()
  818.             endian = file.read(1)
  819.         else:
  820.             return { }
  821.     else:
  822.         return { }
  823.     if debug:
  824.         print {
  825.             'I': 'Intel',
  826.             'M': 'Motorola' }[endian], 'format'
  827.     
  828.     hdr = EXIF_header(file, endian, offset, debug)
  829.     ifd_list = hdr.list_IFDs()
  830.     ctr = 0
  831.     for i in ifd_list:
  832.         if ctr == 0:
  833.             IFD_name = 'Image'
  834.         elif ctr == 1:
  835.             IFD_name = 'Thumbnail'
  836.             thumb_ifd = i
  837.         else:
  838.             IFD_name = 'IFD %d' % ctr
  839.         if debug:
  840.             print ' IFD %d (%s) at offset %d:' % (ctr, IFD_name, i)
  841.         
  842.         hdr.dump_IFD(i, IFD_name)
  843.         exif_off = hdr.tags.get(IFD_name + ' ExifOffset')
  844.         if exif_off:
  845.             if debug:
  846.                 print ' EXIF SubIFD at offset %d:' % exif_off.values[0]
  847.             
  848.             hdr.dump_IFD(exif_off.values[0], 'EXIF')
  849.             intr_off = hdr.tags.get('EXIF SubIFD InteroperabilityOffset')
  850.             if intr_off:
  851.                 if debug:
  852.                     print ' EXIF Interoperability SubSubIFD at offset %d:' % intr_off.values[0]
  853.                 
  854.                 hdr.dump_IFD(intr_off.values[0], 'EXIF Interoperability', dict = INTR_TAGS)
  855.             
  856.         
  857.         gps_off = hdr.tags.get(IFD_name + ' GPSInfo')
  858.         if gps_off:
  859.             if debug:
  860.                 print ' GPS SubIFD at offset %d:' % gps_off.values[0]
  861.             
  862.             hdr.dump_IFD(gps_off.values[0], 'GPS', dict = GPS_TAGS)
  863.         
  864.         ctr += 1
  865.     
  866.     thumb = hdr.tags.get('Thumbnail Compression')
  867.     if thumb and thumb.printable == 'Uncompressed TIFF':
  868.         hdr.extract_TIFF_thumbnail(thumb_ifd)
  869.     
  870.     thumb_off = hdr.tags.get('Thumbnail JPEGInterchangeFormat')
  871.     if thumb_off:
  872.         file.seek(offset + thumb_off.values[0])
  873.         size = hdr.tags['Thumbnail JPEGInterchangeFormatLength'].values[0]
  874.         hdr.tags['JPEGThumbnail'] = file.read(size)
  875.     
  876.     if hdr.tags.has_key('EXIF MakerNote'):
  877.         hdr.decode_maker_note()
  878.     
  879.     if not hdr.tags.has_key('JPEGThumbnail'):
  880.         thumb_off = hdr.tags.get('MakerNote JPEGThumbnail')
  881.         if thumb_off:
  882.             file.seek(offset + thumb_off.values[0])
  883.             hdr.tags['JPEGThumbnail'] = file.read(thumb_off.field_length)
  884.         
  885.     
  886.     return hdr.tags
  887.  
  888. if __name__ == '__main__':
  889.     import sys
  890.     if len(sys.argv) < 2:
  891.         print 'Usage: %s files...\n' % sys.argv[0]
  892.         sys.exit(0)
  893.     
  894.     for filename in sys.argv[1:]:
  895.         
  896.         try:
  897.             file = open(filename, 'rb')
  898.         except:
  899.             print filename, 'unreadable'
  900.             print 
  901.             continue
  902.  
  903.         print filename + ':'
  904.         data = process_file(file)
  905.         if not data:
  906.             print 'No EXIF information found'
  907.             continue
  908.         
  909.         x = data.keys()
  910.         x.sort()
  911.         for i in x:
  912.             if i in ('JPEGThumbnail', 'TIFFThumbnail'):
  913.                 continue
  914.             
  915.             
  916.             try:
  917.                 print '   %s (%s): %s' % (i, FIELD_TYPES[data[i].field_type][2], data[i].printable)
  918.             continue
  919.             print 'error', i, '"', data[i], '"'
  920.             continue
  921.  
  922.         
  923.         if data.has_key('JPEGThumbnail'):
  924.             print 'File has JPEG thumbnail'
  925.         
  926.         print 
  927.     
  928.  
  929.